"""
项目类型分类器

用途：根据给出的文本内容（或 SubDetailItem 的 HTML）判断所属的文件类型，
并输出结构化结果（类型、置信度、理由）。
"""

from typing import Dict, Iterable, List, Optional
from spider.models import SubDetailItem
from process.ai.unified import chat_with_usage
import json
import re


# 可识别的类型枚举（与业务模块对齐）
CLASS_TYPES = [
    "winning_result_announcement",  # 中标结果公示
    "tender_announcement",  # 招标公告
    "bid_opening_record",  # 开标记录
    "bid_opening_detail",  # 开标详情
    "bid_evaluation_result",  # 评标结果
    "bid_candidate",  # 中标候选人
    "transaction_result",  # 合同签约
    "tender_clarification",  # 澄清文件
    "other",  # 其他/无法判断
]


# 默认提示配置（可被入参覆盖）
DEFAULT_SYSTEM_MSG = (
    "你是一名政府采购/工程招标领域的文件类型识别专家。"
    "请基于页面内容与领域常识，判断其所属类型并说明理由，避免编造。"
)

DEFAULT_INSTRUCTION = (
    "阅读提供的页面文本（HTML）与上下文，判断其最可能的所属文件类型。"
    f"可选类型：{', '.join(CLASS_TYPES)}。"
    "如果文本噪声较多，请基于关键词与结构化线索进行判断。"
)

DEFAULT_OUTPUT_FORMAT = (
    "输出 JSON：{type: 类型字符串, confidence: 0-1浮点数, reason: 判断理由}。"
    f"其中 type 必须是以下之一：{', '.join(CLASS_TYPES)}。"
)


def build_messages(
    item: Optional[SubDetailItem] = None,
    text: Optional[str] = None,
    system: Optional[str] = None,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
) -> List[Dict[str, str]]:
    """构建类型识别的消息。可传入 SubDetailItem 或直接传入文本。"""
    sys_msg = system or DEFAULT_SYSTEM_MSG
    inst = instruction or DEFAULT_INSTRUCTION
    fmt = output_format or DEFAULT_OUTPUT_FORMAT
    ctx = extra_context or {}
    content_parts: List[str] = [
        "Instruction:\n" + inst,
        "OutputFormat:\n" + fmt,
        "Context:\n" + json.dumps(ctx, ensure_ascii=False),
    ]
    if item is not None:
        content_parts.append("URL:\n" + item.url)
        content_parts.append("HTML:\n" + item.html_content)
    elif text is not None:
        content_parts.append("TEXT:\n" + text)
    else:
        content_parts.append("TEXT:\n")
    user_content = "\n".join(content_parts)
    return [
        {"role": "system", "content": sys_msg},
        {"role": "user", "content": user_content},
    ]


def _parse_json(text: str) -> Dict:
    if not text:
        return {}
    s = str(text).strip()
    if s.startswith("```"):
        s = s.split("\n", 1)[1] if "\n" in s else s
        if s.endswith("```"):
            s = s[:-3].strip()
    s = re.sub(r"^\s*<think>[\s\S]*?</think>\s*", "", s)
    m = re.search(r"\{[\s\S]*\}", s)
    if m:
        frag = m.group(0)
        try:
            return json.loads(frag)
        except Exception:
            pass
    try:
        return json.loads(s)
    except Exception:
        return {}


def classify_item(
    item: SubDetailItem,
    system: Optional[str] = None,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    model: str = "qwen-plus",
    **kwargs,
) -> Dict[str, object]:
    r = chat_with_usage(
        messages=build_messages(
            item=item,
            system=system,
            instruction=instruction,
            output_format=output_format,
            extra_context=extra_context,
        ),
        model=model,
        **kwargs,
    )
    payload = _parse_json(r.get("content") or "")
    t = payload.get("type") or "other"
    # 归一化到枚举集合
    if t == "winning":
        t = "winning_result_announcement"
    if t not in CLASS_TYPES:
        t = "other"
    confidence = payload.get("confidence")
    try:
        confidence = float(confidence) if confidence is not None else None
    except Exception:
        confidence = None
    reason = payload.get("reason") or ""
    return {"type": t, "confidence": confidence, "reason": reason, "payload": payload, "usage": r.get("usage") or {}}


def classify_text(
    text: str,
    system: Optional[str] = None,
    instruction: Optional[str] = None,
    output_format: Optional[str] = None,
    extra_context: Optional[Dict[str, str]] = None,
    model: str = "qwen-plus",
    **kwargs,
) -> Dict[str, object]:
    r = chat_with_usage(
        messages=build_messages(
            text=text,
            system=system,
            instruction=instruction,
            output_format=output_format,
            extra_context=extra_context,
        ),
        model=model,
        **kwargs,
    )
    payload = _parse_json(r.get("content") or "")
    t = payload.get("type") or "other"
    if t not in CLASS_TYPES:
        t = "other"
    confidence = payload.get("confidence")
    try:
        confidence = float(confidence) if confidence is not None else None
    except Exception:
        confidence = None
    reason = payload.get("reason") or ""
    return {"type": t, "confidence": confidence, "reason": reason, "payload": payload, "usage": r.get("usage") or {}}


__all__ = [
    "CLASS_TYPES",
    "build_messages",
    "classify_item",
    "classify_text",
]
